# This Python file uses the following encoding: utf-8
import heapq
import os

import fasttext
import pandas as pd
import numpy as np
import torch
from torch_geometric.data import Data
from torch_geometric.nn import GCNConv


# 将每一份招聘要求和简历技能构建成图，利用图卷积网络模型，进行各节点间特征结合和特征转换
class GCN(torch.nn.Module):
    def __init__(self):
        super().__init__()
        torch.manual_seed(1234)
        self.conv1 = GCNConv(100, 70)  # 将输入的一百维特征转化为七十维特征
        self.conv2 = GCNConv(70, 10)  # 将输入的七十维特征转化为十维特征
        self.conv3 = GCNConv(10, 5)  # 将输入的十维特征转化为五维特征
        self.classifier = torch.nn.Linear(5, 30)  # 将输入的五维特征转化为三十维特征

    def forward(self, x, edge_index):
        h = self.conv1(x, edge_index)
        h = h.tanh()
        h = self.conv2(h, edge_index)
        h = h.tanh()
        h = self.conv3(h, edge_index)
        h = h.tanh()
        h = self.classifier(h)
        return h


class Recommendation:

    def transform_features(self, data, model):  # 调用模型得到特征
        """
        Transform the features
        :param data: 传入模型的数据
        :param model: 需传入的模型
        :return: 传入模型后得到的特征值
        """
        transformed_h = model(data.x, data.edge_index)
        return transformed_h

    def getData(self, mydata, Job, model):  # 构建数据集,mydata为一条数据，该函数得到该数据的Data类表示,Job为该数据对应的工作名称
        """
        将简历技能或招聘要求转化成可以传入模型的数据
        :param mydata:一条简历技能数据或招聘要求数据
        :param Job:数据对应的岗位
        :param model:对应技能的词向量模型
        :return:
        """
        x = []
        for word in mydata:
            word = word.replace(",", "")
            temp = model.get_word_vector(word)
            x.append(temp)
        x.append(np.zeros(100, dtype=np.float32))
        x = np.array(x)
        x = torch.tensor(x)
        length = len(mydata)
        resource = [length] * length
        target = [i for i in range(length)]
        edge_index = torch.tensor([resource, target], dtype=torch.long)
        data = Data(x=x, edge_index=edge_index)
        return data

    def getData1(self, mydata, model):  # 构建数据集,mydata为一条数据，该函数得到该数据的Data类表示,Job为该数据对应的工作名称
        """
        将简历技能或招聘要求转化成可以传入模型的数据
        :param mydata:一条简历技能数据或招聘要求数据
        :param Job:数据对应的岗位
        :param model:对应技能的词向量模型
        :return:
        """
        x = []

        for word in mydata:
            word = word.replace(",", "")
            temp = model.get_word_vector(word)
            x.append(temp)
        x.append(np.zeros(100, dtype=np.float32))
        x = np.array(x)
        x = torch.tensor(x)
        length = len(mydata)
        resource = [length] * length
        target = [i for i in range(length)]
        edge_index = torch.tensor([resource, target], dtype=torch.long)
        data = Data(x=x, edge_index=edge_index)
        return data

    def cosine_similarity(self, vec1, vec2):  # 计算余弦相似度
        """
        计算俩个向量的余弦相似度
        :param vec1: 向量1
        :param vec2: 向量2
        :return: 俩向量的余弦相似度
        """
        dot_product = np.dot(vec1, vec2)
        norm_a = np.linalg.norm(vec1)
        norm_b = np.linalg.norm(vec2)
        return dot_product / (norm_a * norm_b)

    def get_finalData(self, input_csv, output_column, word_model_file):
        """
        读取可直接使用的数据文件，得到最终的数据集
        :param input_csv: 读取文件
        :param output_column: 读取列,其中第一个元素需要为构建数据集的数据列,第二个为对应数据的岗位列，第3个为对应的id列
        :param word_model_file:进行处理的词向量模型
        :return: 最终数据集
        """
        # 导入词向量模型
        model = fasttext.load_model(word_model_file)
        # 加载csv文件
        df = pd.read_csv(input_csv)
        column1 = [text.split() for text in df[output_column[0]]]
        column2 = [text for text in df[output_column[1]]]
        column3 = [text for text in df[output_column[2]]]
        data_all = []
        for i in range(len(column1)):
            data = self.getData(column1[i], column2[i], model)
            data_all.append(data)
        return data_all, column3

    def model_use(self, data_all):
        """
        将数据集的数据传入模型
        :param data_all: 数据集
        :return: 经过模型得到数据集里每一个图的全局特征
        """
        model = GCN()  # 构建图卷积网络模型
        torch.save(model.state_dict(), 'gcn_model.pth')
        data_column_means = []
        for i in range(len(data_all)):
            # 使用模型进行特征转换
            transformed_data_features = self.transform_features(data_all[i], model)
            # 得到全局特征
            column_means = transformed_data_features.mean(dim=0)
            data_column_means.append(column_means.detach().numpy())
        return data_column_means

    def save_features(self, save_file, data_column_means):
        """
        保存文件
        :param save_file: 保存的文件(.npz)
        :param data_data_column_means:保存的数组
        :return:none
        """
        np.savez(save_file, *data_column_means)

    def load_features(self, load_file):
        """
        导入文件
        :param load_file: 导入文件
        :return: 数据集的全局特征
        """
        # 后续读取
        loaded_arrays = np.load(load_file)

        # 将加载的各个数组重新整理成一个列表
        data_column_means = [arr for arr in loaded_arrays.values()]
        return data_column_means

    def get_compare_values(self, mydata, Job, model_file, word_model_file):
        """
        处理推荐处信息时，将其传入model中得到全局特征
        :param mydata: 一条简历技能数据或招聘要求数据
        :param Job: 数据对应的岗位
        :param model_file: GCN模型参数文件
        :param word_model_file: 对应技能的词向量模型参数文件
        :return:
        """
        # 导入词向量模型
        model_use = fasttext.load_model(word_model_file)
        userdata = self.getData(mydata, Job, model_use)
        model = GCN()
        model.load_state_dict(torch.load(model_file))
        # 使用模型进行特征转换
        transformed_data_features = self.transform_features(userdata, model)
        # 得到全局特征
        compare_values = transformed_data_features.mean(dim=0)
        return compare_values.detach().numpy()

    def get_compare_values_user(self, mydata, model_file, word_model_file):
        """
        处理推荐处信息时，将其传入model中得到全局特征
        :param mydata: 一条简历技能数据或招聘要求数据
        :param Job: 数据对应的岗位
        :param model_file: GCN模型参数文件
        :param word_model_file: 对应技能的词向量模型参数文件
        :return:
        """
        # 导入词向量模型
        model_use = fasttext.load_model(word_model_file)
        userdata = self.getData1(mydata, model_use)
        model = GCN()
        model.load_state_dict(torch.load(model_file))
        # 使用模型进行特征转换
        transformed_data_features = self.transform_features(userdata, model)
        # 得到全局特征
        compare_values = transformed_data_features.mean(dim=0)
        return compare_values.detach().numpy()

    def getmax_similarity(self, data_column_means, compare_values):  # 得到compare_values与其他数据的相似度列表
        """
        compare_values是比较特征，与data_column_means中的数据进行相似度比较，得到列表结果
        :param data_column_means:数据集特征
        :param compare_values:需进行相似度比较的数据特征
        :return:列表
        """
        similarity_result = []
        # i_result = []
        for i in range(len(data_column_means)):
            similarity = self.cosine_similarity(data_column_means[i], compare_values)
            similarity_result.append(similarity)
            # i_result.append(i)
        return similarity_result

    def get_recommendation(self, data_column_means, compare_values, result_csv, result_columns, n, operation,
                           use_xueli):
        """
        得到推荐结果，输出结果，返回对应的下标值
        :param data_column_means:数据集全局特征
        :param compare_values:比较数据全局特征
        :param result_csv:结果所在的表
        :param result_columns:表需读取的列,第一个数据为ID信息
        :param n:推荐的个数
        :param operation:推荐时需满足要求的操作函数
        :param use_xueli:需要的学历
        :return:推荐数据的ID
        """
        similarity_result = self.getmax_similarity(data_column_means, compare_values)
        largest_values_indices = heapq.nlargest(len(similarity_result),
                                                ((v, i) for i, v in enumerate(similarity_result)))
        # 分离最大值和下标
        largest_values, indices = zip(*largest_values_indices)
        search_Result = self.myresult(indices, result_csv, result_columns, n, operation, use_xueli)
        return search_Result

    def myresult(self, in_id, result_csv, result_columns, n, operation, use_xueli):

        # 读取CSV文件，并指定只读取所需的列
        specific_columns = result_columns
        df = pd.read_csv(result_csv, usecols=specific_columns)

        # 将DataFrame中的数据类型转换为JSON支持的数据类型
        df = df.astype({
            'ID': str,  # 将ID列的数据类型转换为字符串类型
            '公司名称': str,  # 将公司名称列的数据类型转换为字符串类型
            '岗位': str,  # 将岗位列的数据类型转换为字符串类型
            'salary': str,  # 将salary列的数据类型转换为浮点数类型
            'education': str,  # 将education列的数据类型转换为字符串类型
            'description': str,  # 将description列的数据类型转换为字符串类型
            'link': str  # 将link列的数据类型转换为字符串类型
        })

        ID_need = []
        i_need = []
        for i in range(len(in_id)):
            row_number = in_id[i]
            if operation(use_xueli, df.loc[row_number, result_columns[4]]):
                ID_need.append(df.loc[row_number, result_columns[0]])
                i_need.append(i)
                if len(ID_need) == n:
                    break

        result_list = []
        for k in i_need:
            row_number = in_id[k]
            result_dict = {}
            for column in result_columns:
                result_dict[column] = df.loc[row_number, column]
            result_list.append(result_dict)
        return result_list



    def compare_degrees(self, degree1: str, degree2: str) -> bool:
        degree_order = {"博士": 4, "硕士": 3, "本科": 2, "大专": 1}
        # Ensure both degrees are valid and in the degree_order dictionary
        if degree1 not in degree_order or degree2 not in degree_order:
            # raise ValueError("Invalid degree specified. Valid degrees are: 博士, 硕士, 本科, 大专.")
            print("errcv")
        return degree_order[degree1] == degree_order[degree2]


def recommend_jobs_based_on_resume(resume_it: list, education: str):
    """
    根据简历技能推荐岗位
    :param resume_it: 简历技能列表
    :param education: 学历要求
    :return: 推荐的岗位ID列表
    """
    # 加载Recommendation类的实例
    test = Recommendation()
    current_dir = os.getcwd()
    parent_dir = os.path.dirname(current_dir)
    # 加载数据集全局特征
    data_column_means = test.load_features(parent_dir+'\\GCN\\my_arrays3.npz')


    # 读取模型参数文件路径
    model_file =parent_dir +'\\GCN\\gcn_model.pth'
    # 读取词向量模型参数文件路径
    word_model_file = parent_dir+'\\GCN\\ail9_1.bin'

    # 获取简历技能对应的全局特征
    user_values = test.get_compare_values_user(resume_it, model_file, word_model_file)

    # 根据学历和简历技能推荐岗位
    search_Result = test.get_recommendation(data_column_means, user_values,
                                            parent_dir+'\\Files\\Prepare\\招聘信息.csv',
                                            ['ID', '公司名称', '岗位', 'salary', 'education',
                                             'hiring_manager', 'city', 'description', 'link', 'address'],
                                            10, test.compare_degrees, education)
    return search_Result
